//
//  Person.h
//  MRC_Test
//
//  Created by admin on 2019/7/30.
//  Copyright © 2019 猫爷爷的酱醋茶. All rights reserved.
//

/*
 
 @property
    作用：
        a.自动生成私有属性
        b.自动生成这个属性的getter setter方法的声明和声明
        c.Xcode4.5前，方法的实现还需要自己手动实现或者用 @synthesize 名称;
    语法：
        @property 数据类型 名称;
            如果数据类型相同，可以批量声明。
        @synthesize 名称; 将参数直接赋值给自动生成的私有属性
            即使数据类型不一致，也能批量声明。
            如果使用语法 @synthesize 名称 = 对象属性名
                       @synthesize age = _age;
            这样不会再自动生成私有属性，而是生成指定的对象属性名
            setter的实现， 把参数的值直接赋值给指定的属性。
            getter的实现，直接返回指定属性的值
    注意：
        @property的数据类型、名称和属性的数据类型和名称（去掉下划线）必须一致。
        因为@property的数据类型和名称决定了生成setter和setter的方法名。
        如果同时重写setter和getter方法，那么就不会自动生成私有属性
        生成的setter方法中，无论是什么类型，都是直接赋值的，所以不能再MRC下使用
 
    继承：
        父类的@property一样可以被子类继承
        但是，@property生成的属性是私有的，在子类内部无法直接访问生成的私有属性
        但是可以通过setter方法 和getter方法访问。
        比如 [super setName:参数]  或者self.name
 
    参数：
        @property(参数1,参数2,参数3,...)数据类型 名称;
 
        a. 与多线程相关的两个参数
            atomic（默认值）原子属性
                - 这个时候生成的setter方法的代码会被加上一把线程安全锁 - 自旋锁。
                单写多读：单个线程写入，多个线程读取
                但是无法保存同时读写的正确性。
                特点：安全  缺点：效率较低。
            nonatomic（推荐） - 非原子属性
                - 这个时候生成的setter方法的代码不会加上线程安全锁
                特点：效率高
                线程安全锁的概念：如果有多个对象同时访问，一次只能被一个对象访问，其余的等待。
 
            尽量将加锁、资源抢夺的业务逻辑交给服务器端处理，减小移动客户端的压力
 
        b. 与生成setter方法的实现相关的参数
            assign（默认值）- 这个时候生成的setter方法的实现就是直接赋值
            retain - 生成的setter方法就是标准的MRC内存管理代码
                    就是先判断新旧对象时候为同一对象，如果不是 release旧的 retain新的
                注意：需要自己手动重写dealloc方法。
            当属性的类型是OC对象类型的时候，那麽就使用retain
 
        c. 与生成只读、读写相关的参数
            readwrite（默认值）代表同时生成getter和setter方法
            readonly - 代表只生成getter方法，不生成setter方法
 
        d. 自定义getter、setter生成的方法名字
            getter = 自定义getter的名字
                    @property(getter = xxx)int age;
            setter = 自定义setter的名字
                    注意setter自带参数需要带：
                    @property(getter = xxx,setter = xx:)int age;
 
            注意：如果使用getter和setter修改了生成的名字，
                在使用点语法赋值的时候，编译器会转化为修改后的方法名字代码。
 
            建议：无论如何都不建议修改setter方法，因为已经是最标准的语法了
                当属性的类型是一个BOOL类型时，可以修改gatter取值方法名，以is开头，提高阅读性。
 情况：
    a）当两个类互相包含的时候，会出现循环引用的问题，就会造成无限递归的问题，而导致无法编译通过。
    解决办法：
         其中一边.h文件不引入对方头文件，而是使用@class 类名，来标注这是一个类，
         这样子就可以在不引入对方头文件的情况下，告诉编译器这是一个类。例Person类，和Book类。
         而在.m文件上可以#import引入头文件，这样调用的时候才会有提示。
 
     b）当两个对象相互引用的时候：A对象是B对象的属性，B对象是A对象的属性
     这个时候如果两边的@property的参数都为retain 那么，就会发生内存泄漏
        解决办法：
        一段使用retain 另外一端使用assign  而使用assign的一段dealloc中就不需要对属性release
 
 类方法的规范：
    一般情况下，要求提供与自定义构造方法相同功能的类方法，这样可以快速创建对象。
    使用类得到的对象，要求这个对象在方法中，就已经被autorelease过了，这样，我们只要在自动释放池中用类调用对象，就自动加入自动释放池了。
 
 */

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@class Book;

@interface Person : NSObject

@property(nonatomic,retain)NSString *name;
@property(nonatomic)int age;
@property(nonatomic,retain)Book *book;

-(void)ReadBook;

-(instancetype)initWithName:(NSString *)name andAge:(int)age andBook:(Book *)book;

+(instancetype)person;

+(instancetype)personWithName:(NSString *)name andAge:(int)age andBook:(Book *)book;

@end

NS_ASSUME_NONNULL_END
